<!doctype html><html lang=zh-hans>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta http-equiv=x-ua-compatible content="IE=edge">
<meta name=author content="小李刀刀">
<meta name=description content="除了通过路由来调用控制器方法（action），Spiral 还允许从服务中甚至是从其它的控制器中调用控制器方法（HMVC: Hierarchical Model View Controller）。但是对控制器方法的方法的调用必须通过 Spiral\Core\CoreInterface 来进行。CoreInterface 以及领域内核使开发者能够更改调用流程，以及为控制器实现特定">
<meta name=theme-color content="#3f51b5">
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/academicons/1.8.6/css/academicons.min.css integrity="sha256-uFVgMKfistnJAfoCUQigIl+JfUaP47GrRKjf6CTPVmw=" crossorigin=anonymous>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-1/css/all.min.css integrity="sha256-4w9DunooKSr3MFXHXWyFER38WmPdm361bQS/2KUWZbU=" crossorigin=anonymous>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css integrity="sha256-Vzbj7sDDS/woiFS3uNKo8eIuni59rjyNGtXfstRzStA=" crossorigin=anonymous>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/styles/tomorrow-night.min.css crossorigin=anonymous title=hl-light>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/styles/tomorrow-night.min.css crossorigin=anonymous title=hl-dark disabled>
<script src=https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/lazysizes.min.js integrity="sha256-Md1qLToewPeKjfAHU1zyPwOutccPAm5tahnaw7Osw0A=" crossorigin=anonymous async></script>
<link rel=stylesheet href="https://fonts.font.im/css?family=Source+Code+Pro:300,400,500,600,700&subset=latin-ext&display=swap">
<link rel=stylesheet href=/css/academic.css>
<script>(function(b,d,e,a,g){b[a]=b[a]||[],b[a].push({'gtm.start':(new Date).getTime(),event:'gtm.js'});var f=d.getElementsByTagName(e)[0],c=d.createElement(e),h=a!='dataLayer'?'&l='+a:'';c.async=!0,c.src='https://www.googletagmanager.com/gtm.js?id='+g+h,f.parentNode.insertBefore(c,f)})(window,document,'script','dataLayer','GTM-5KZH8N7')</script>
<link rel=manifest href=/index.webmanifest>
<link rel=icon type=image/png href=/images/icon_hu0b7a4cb9992c9ac0e91bd28ffd38dd00_9727_32x32_fill_lanczos_center_3.png>
<link rel=apple-touch-icon type=image/png href=/images/icon_hu0b7a4cb9992c9ac0e91bd28ffd38dd00_9727_192x192_fill_lanczos_center_3.png>
<link rel=canonical href=https://studyspiral.cn/docs/cookbook/domain-core/>
<meta property="twitter:card" content="summary">
<meta property="og:site_name" content="Spiral中文网">
<meta property="og:url" content="https://studyspiral.cn/docs/cookbook/domain-core/">
<meta property="og:title" content="速查手册 - 领域内核 | Spiral中文网">
<meta property="og:description" content="除了通过路由来调用控制器方法（action），Spiral 还允许从服务中甚至是从其它的控制器中调用控制器方法（HMVC: Hierarchical Model View Controller）。但是对控制器方法的方法的调用必须通过 Spiral\Core\CoreInterface 来进行。CoreInterface 以及领域内核使开发者能够更改调用流程，以及为控制器实现特定"><meta property="og:image" content="https://studyspiral.cn/images/logo.svg">
<meta property="twitter:image" content="https://studyspiral.cn/images/logo.svg"><meta property="og:locale" content="zh-Hans">
<meta property="article:published_time" content="2020-03-01T20:45:12+08:00">
<meta property="article:modified_time" content="2020-03-25T03:56:47+08:00">
<script src=https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.1/cookieconsent.min.js integrity="sha256-5VhCqFam2Cn+yjw61zbBNrbHVJ6SRydPeKopYlngbiQ=" crossorigin=anonymous></script>
<link rel=stylesheet href=https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.1/cookieconsent.min.css integrity="sha256-zQ0LblD/Af8vOppw18+2anxsuaz3pWYyVWi+bTvTH8Q=" crossorigin=anonymous>
<script>window.addEventListener("load",function(){window.cookieconsent.initialise({palette:{popup:{background:"#3f51b5",text:"#fff"},button:{background:"#fff",text:"#3f51b5"}},theme:"classic",content:{message:"本网站使用cookies来确保您在本网站上获得最佳体验。",dismiss:"知道了!",link:"了解更多",href:"https://www.cookiesandyou.com"}})})</script>
<title>速查手册 - 领域内核 | Spiral中文网</title>
</head>
<body id=top data-spy=scroll data-offset=70 data-target=#TableOfContents>
<aside class=search-results id=search>
<div class=container>
<section class=search-header>
<div class="row no-gutters justify-content-between mb-3">
<div class=col-6>
<h1>搜索</h1>
</div>
<div class="col-6 col-search-close">
<a class=js-search href=#><i class="fas fa-times-circle text-muted" aria-hidden=true></i></a>
</div>
</div>
<div id=search-box>
<input name=q id=search-query placeholder=搜索... autocapitalize=off autocomplete=off autocorrect=off spellcheck=false type=search>
</div>
</section>
<section class=section-search-results>
<div id=search-hits>
</div>
</section>
</div>
</aside>
<nav class="navbar navbar-expand-lg navbar-light compensate-for-scrollbar" id=navbar-main>
<div class=container>
<div class="d-none d-lg-inline-flex">
<a class=navbar-brand href=/><img src=/images/logo.svg alt=Spiral中文网>Spiral中文网</a>
</div>
<button type=button class=navbar-toggler data-toggle=collapse data-target=#navbar-content aria-controls=navbar aria-expanded=false aria-label=切换导航>
<span><i class="fas fa-bars"></i></span>
</button>
<div class="navbar-brand-mobile-wrapper d-inline-flex d-lg-none">
<a class=navbar-brand href=/><img src=/images/logo.svg alt=Spiral中文网>Spiral中文网</a>
</div>
<div class="navbar-collapse main-menu-item collapse justify-content-end" id=navbar-content>
<ul class="navbar-nav d-md-inline-flex">
<li class=nav-item>
<a class=nav-link href=/docs/basics/quick-start/><span>教程</span></a>
</li>
<li class=nav-item>
<a class="nav-link active" href=/docs/><span>文档</span></a>
</li>
<li class=nav-item>
<a class=nav-link href=/post/><span>文章</span></a>
</li>
</ul>
</div>
<ul class="nav-icons navbar-nav flex-row ml-auto d-flex pl-md-2">
<li class=nav-item>
<a class="nav-link js-search" href=#><i class="fas fa-search" aria-hidden=true></i></a>
</li>
</ul>
</div>
</nav>
<div class="container-fluid docs">
<div class="row flex-xl-nowrap">
<div class="col-12 col-md-3 col-xl-2 docs-sidebar">
<form class="docs-search d-flex align-items-center">
<button class="btn docs-toggle d-md-none p-0 mr-3" type=button data-toggle=collapse data-target=#docs-nav aria-controls=docs-nav aria-expanded=false aria-label="Toggle section navigation">
<span><i class="fas fa-bars"></i></span>
</button>
<input name=q type=search class=form-control placeholder=搜索... autocomplete=off>
</form>
<nav class="collapse docs-links" id=docs-nav>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/extension/dotenv/>Dotenv</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/>总览</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse show">
<li>
<a href=/docs/>Spiral介绍</a>
</li>
<li>
<a href=/docs/about/contributing/>贡献指引</a>
</li>
<li>
<a href=/docs/about/semver/>版本说明</a>
</li>
<li>
<a href=/docs/about/license/>授权协议</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/http/psr-15/>定制 PSR-15 处理器</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/start/install/>快速开始</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/start/install/>安装指引</a>
</li>
<li>
<a href=/docs/start/workers/>应用工作进程</a>
</li>
<li>
<a href=/docs/start/structure/>应用程序结构</a>
</li>
<li>
<a href=/docs/start/configuration/>默认配置</a>
</li>
<li>
<a href=/docs/start/commands/>控制台命令</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/basics/quick-start/>入门基础</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/basics/quick-start/>上手指南</a>
</li>
<li>
<a href=/docs/basics/scaffolding/>脚手架</a>
</li>
<li>
<a href=/docs/basics/prototype/>原型开发辅助</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/framework/design/>核心框架</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/framework/design/>设计理念</a>
</li>
<li>
<a href=/docs/framework/application-server/>应用服务器</a>
</li>
<li>
<a href=/docs/framework/config/>配置对象</a>
</li>
<li>
<a href=/docs/framework/kernel/>内核与环境</a>
</li>
<li>
<a href=/docs/framework/container/>容器</a>
</li>
<li>
<a href=/docs/framework/bootloaders/>引导程序</a>
</li>
<li>
<a href=/docs/framework/scopes/>IoC 作用域</a>
</li>
<li>
<a href=/docs/framework/memory/>静态高速缓存</a>
</li>
<li>
<a href=/docs/framework/finalizers/>终结器</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/cookbook/annotated-routes/>速查手册</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse show">
<li>
<a href=/docs/cookbook/annotated-routes/>注解式路由</a>
</li>
<li>
<a href=/docs/cookbook/injector/>容器注入器</a>
</li>
<li class=active>
<a href=/docs/cookbook/domain-core/>领域内核、控制器</a>
</li>
<li>
<a href=/docs/cookbook/golang-library/>Golang服务集成</a>
</li>
<li>
<a href=/docs/cookbook/custom-dispatcher/>自定义调度器</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/component/files/>常用组件</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/component/files/>文件和目录</a>
</li>
<li>
<a href=/docs/component/reactor/>代码生成</a>
</li>
<li>
<a href=/docs/component/tokenizer/>静态分析工具</a>
</li>
<li>
<a href=/docs/component/metrics/>应用指标</a>
</li>
<li>
<a href=/docs/component/data-grid/>数据网格</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/console/configuration/>控制台</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/console/configuration/>安装和配置</a>
</li>
<li>
<a href=/docs/console/commands/>用户命令</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/http/configuration/>HTTP</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/http/configuration/>安装配置</a>
</li>
<li>
<a href=/docs/http/lifecycle/>请求生命周期</a>
</li>
<li>
<a href=/docs/http/request-response/>请求和响应</a>
</li>
<li>
<a href=/docs/http/routing/>路由</a>
</li>
<li>
<a href=/docs/http/errors/>错误页面</a>
</li>
<li>
<a href=/docs/http/middleware/>中间件</a>
</li>
<li>
<a href=/docs/http/golang/>Golang 中间件</a>
</li>
<li>
<a href=/docs/http/cookies/>Cookies</a>
</li>
<li>
<a href=/docs/http/session/>Session</a>
</li>
<li>
<a href=/docs/http/csrf/>CSRF 防护</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/security/encrypter/>安全</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/security/encrypter/>数据加密</a>
</li>
<li>
<a href=/docs/security/validation/>数据验证</a>
</li>
<li>
<a href=/docs/security/rbac/>基于角色的权限控制</a>
</li>
<li>
<a href=/docs/security/authentication/>用户认证</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/filters/configuration/>请求过滤</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/filters/configuration/>安装和配置</a>
</li>
<li>
<a href=/docs/filters/filter/>过滤器</a>
</li>
<li>
<a href=/docs/filters/composite/>复合过滤器</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/database/configuration/>数据库</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/database/configuration/>安装与配置</a>
</li>
<li>
<a href=/docs/database/access/>访问数据</a>
</li>
<li>
<a href=/docs/database/isolation/>逻辑隔离</a>
</li>
<li>
<a href=/docs/database/query-builders/>查询构造器</a>
</li>
<li>
<a href=/docs/database/transactions/>Transactions</a>
</li>
<li>
<a href=/docs/database/introspection/>Schema Introspection</a>
</li>
<li>
<a href=/docs/database/declaration/>Declaration</a>
</li>
<li>
<a href=/docs/database/migrations/>Migrations</a>
</li>
<li>
<a href=/docs/database/errata/>Errata</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/cycle/configuration/>Cycle ORM</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/cycle/configuration/>Configuration</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/queue/configuration/>队列任务</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/queue/configuration/>Configuration</a>
</li>
<li>
<a href=/docs/queue/scraper/>网站爬虫</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/views/configuration/>视图</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/views/configuration/>Configuration</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link href=/docs/stempler/configuration/>Stempler 模板</a>
<ul class="nav docs-sidenav docs-sidenav-sub collapse">
<li>
<a href=/docs/stempler/configuration/>Configuration</a>
</li>
<li>
<a href=/docs/stempler/directives/>Directives</a>
</li>
</ul>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>国际化</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>GRPC</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>事件广播</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>调试及性能</a>
</div>
<div class=docs-toc-item>
<a class=docs-toc-link>扩展</a>
</div>
</nav>
</div>
<div class="d-none d-xl-block col-xl-2 docs-toc">
<ul class="nav toc-top">
<li><a href=# id=back_to_top class=docs-toc-title>在本页</a></li>
</ul>
<nav id=TableOfContents>
<ul>
<li><a href=#调用控制器方法>调用控制器方法</a></li>
<li><a href=#内核拦截器>内核拦截器</a></li>
<li><a href=#全局领域内核>全局领域内核</a>
<ul>
<li><a href=#路由级领域内核>路由级领域内核</a></li>
</ul>
</li>
<li><a href=#领域内核构建器>领域内核构建器</a>
<ul>
<li><a href=#cycle-实体解析>Cycle 实体解析</a></li>
<li><a href=#过滤器验证>过滤器验证</a></li>
<li><a href=#访问权限拦截器>访问权限拦截器</a></li>
</ul>
</li>
<li><a href=#综合应用>综合应用</a></li>
</ul>
</nav>
</div>
<main class="col-12 col-md-9 col-xl-8 py-md-3 pl-md-5 docs-content" role=main>
<article class=article>
<div class="alert alert-warning" role=alert>
官方文档中文版翻译工作仍在进行中，欢迎 <a href=/post/join-translation/>参与翻译</a>。
</div>
<div class=docs-article-container>
<h1>速查手册 - 领域内核</h1>
<div class=article-style>
<p>除了通过路由来调用控制器方法（action），Spiral 还允许从服务中甚至是从其它的控制器中调用控制器方法（HMVC: Hierarchical Model View Controller）。但是对控制器方法的方法的调用必须通过 <code>Spiral\Core\CoreInterface</code> 来进行。<code>CoreInterface</code> 以及<em>领域内核</em>使开发者能够更改调用流程，以及为控制器实现特定于域的功能。</p>
<blockquote>
<p>领域内核依赖 <code>spiral/hmvc</code> 组件。官方 Web 应用模板中已经默认包含了这个组件。</p>
</blockquote>
<h2 id=调用控制器方法>调用控制器方法</h2>
<p>Spiral 中的控制器都是普通的类，供调度程序调用。框架本身并不提供路由和控制器之间的直接耦合。这样的实现方式下，就可以在必要的时候在代码中直接调用控制器中定义的方法。</p>
<pre><code class=language-php>namespace App\Controller;

use Spiral\Core\CoreInterface;

class HomeController
{
    public function index(CoreInterface $core)
    {
        return &quot;Index: &quot; . $core-&gt;callAction(HomeController::class, &quot;other&quot;, [&quot;name&quot; =&gt; &quot;Antony&quot;]);
        // 这行代码最终返回 &quot;Index: Hello, Antony&quot;
    }

    public function other(string $name)
    {
        return sprintf(&quot;Hello, %s&quot;, $name);
    }
}
</code></pre>
<p>默认状态下，<code>CoreInterface</code> 接口由 <code>Spiral\Core\Core</code> 这个类实现，它只提供方法注入。</p>
<h2 id=内核拦截器>内核拦截器</h2>
<p>为了实现自定义的调用逻辑，需要借助 <code>Spiral\Core\CoreInterceptorInterface</code> 接口 <code>Spiral\Core\InterceptableCore</code> 类。</p>
<p>首先创建一个实现 <code>Spiral\Core\CoreInterceptorInterface</code> 的自定义拦截器：</p>
<pre><code class=language-php>namespace App\Interceptor;

use Spiral\Core\CoreInterceptorInterface;
use Spiral\Core\CoreInterface;

class CustomInterceptor implements CoreInterceptorInterface
{
    public function process(string $controller, string $action, array $parameters, CoreInterface $core)
    {
        return 'intercepted: ' . $core-&gt;callAction($controller, $action, $parameters);
    }
}
</code></pre>
<p>接下来通过创建一个 <code>InterceptableCore</code> 类的实例来使用我们的自定义拦截器：</p>
<pre><code class=language-php>namespace App\Controller;

use App\Interceptor\CustomInterceptor;
use Spiral\Core\CoreInterface;
use Spiral\Core\InterceptableCore;

class HomeController
{
    public function index(CoreInterface $core)
    {
        $customCore = new InterceptableCore($core);
        $customCore-&gt;addInterceptor(new CustomInterceptor());

        // 截获执行结果：Hello, Antony
        return $customCore-&gt;callAction(HomeController::class, &quot;other&quot;, [&quot;name&quot; =&gt; &quot;Antony&quot;]);
    }

    public function other(string $name)
    {
        return sprintf(&quot;Hello, %s&quot;, $name);
    }
}
</code></pre>
<p>通过拦截器可以针对控制器、控制器方法和参数进行改动。根据需要也可使用多个拦截器。</p>
<h2 id=全局领域内核>全局领域内核</h2>
<p>默认情况下，<code>CoreInterface</code> 只作用于 Spiral 框架路由的目标。通过把 <code>Spiral\Core\CoreInterface</code> 绑定到自定义的内核类，可以改变目标：</p>
<pre><code class=language-php>namespace App\Bootloader;

use App\Interceptor\CustomInterceptor;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Core\Core;
use Spiral\Core\CoreInterface;
use Spiral\Core\InterceptableCore;

class CoreBootloader extends Bootloader
{
    protected const SINGLETONS = [
        CoreInterface::class =&gt; [self::class, 'core']
    ];

    private function core(Core $core): CoreInterface
    {
        $customCore = new InterceptableCore($core);
        $customCore-&gt;addInterceptor(new CustomInterceptor());

        return $customCore;
    }
}
</code></pre>
<p>别忘了激活上面的引导程序，激活之后即可拦截所有的路由目标。</p>
<h3 id=路由级领域内核>路由级领域内核</h3>
<p>除了在引导程序中激活的全局领域内核以外，也可以只针对特定路由激活领域内核：</p>
<pre><code class=language-php>$customCore = new InterceptableCore($core);
$customCore-&gt;addInterceptor(new CustomInterceptor());

$router-&gt;setRoute(
    'home',
    new Route(
        '/home/&lt;action&gt;',
        (new Controller(HomeController::class))-&gt;withCore($customCore)
    )
);
</code></pre>
<h2 id=领域内核构建器>领域内核构建器</h2>
<p>Spiral 提供了 <code>Spiral\Bootloader\DomainBootloader</code> 这个引导程序，可以很方便地自动配置核心拦截器，可以创建一个继承 <code>DomainBootloader</code> 的引导程序：</p>
<pre><code class=language-php>namespace App\Bootloader;

use App\Interceptor\CustomInterceptor;
use Spiral\Bootloader\DomainBootloader;
use Spiral\Core\CoreInterface;

class AppBootloader extends DomainBootloader
{
    protected const SINGLETONS = [
        CoreInterface::class =&gt; [self::class, 'domainCore']
    ];

    protected const INTERCEPTORS = [
        CustomInterceptor::class
    ];
}
</code></pre>
<p>把要配置的拦截器类添加到该引导程序的 <code>INTEREPTORS</code> 集合中，引导程序通过这一组默认拦截器即可全局性地配置应用程序行为。</p>
<h3 id=cycle-实体解析>Cycle 实体解析</h3>
<p>使用 <code>Spiral\Domain\CycleInterceptor</code> 拦截器，可以根据参数自动解析和注入 Cycle 实体：</p>
<pre><code class=language-php>$router-&gt;setRoute(
    'home',
      new Route(
          '/home/&lt;action&gt;/&lt;id&gt;',
          new Controller(HomeController::class)
      )
);
</code></pre>
<p>按照前文介绍的方法，把 <code>CycleInterceptor</code> 加入到默认拦截器集合中：</p>
<pre><code class=language-php>namespace App\Bootloader;

use Spiral\Bootloader\DomainBootloader;
use Spiral\Core\CoreInterface;
use Spiral\Domain\CycleInterceptor;

class AppBootloader extends DomainBootloader
{
    protected const SINGLETONS = [
        CoreInterface::class =&gt; [self::class, 'domainCore']
    ];

    protected const INTERCEPTORS = [
        CycleInterceptor::class
    ];
}
</code></pre>
<p>然后即可在控制器方法中直接依赖 Cycle 实体，系统会把路由中的 <code>&lt;id></code> 参数作为实体的主键，根据声明的类型自动解析出对应的实体对象并注入到方法。如果指定的主键没有查询到实体，会抛出 404 错误。</p>
<pre><code class=language-php>namespace App\Controller;

use App\Database\User;

class HomeController
{
    public function index(User $user)
    {
        dump($user);
    }
}
</code></pre>
<p>要使用的实体超过一个时，路由参数就不能再使用 <code>&lt;id></code>，而必须使用命名参数：</p>
<pre><code class=language-php>$router-&gt;setRoute(
    'home',
      new Route(
          '/home/&lt;action&gt;/&lt;user&gt;/&lt;author&gt;',
          new Controller(HomeController::class)
      )
);
</code></pre>
<p>控制器方法中的参数名要和路由参数名对应：</p>
<pre><code class=language-php>namespace App\Controller;

use App\Database\User;
use App\Database\Post;

class HomeController
{
    public function index(User $user, User $author)
    {
        dump($user);
    }
}
</code></pre>
<h3 id=过滤器验证>过滤器验证</h3>
<p>Spiral 提供了 <code>Spiral\Filter\FilterInterface</code> 接口和 <code>Spiral\Domain\FilterInterceptor</code> 拦截器，开发者使用继承 <code>Spiral\Filter\Filter</code> 创建请求类，就可以实现请求参数的自动验证和过滤。如果参数检查失败，错误会以 JSON 形式返回（如果要改变这一行为，可以继承 <code>FilterInterceptor</code> 来创建自己的拦截器）。使用方法如下：</p>
<pre><code class=language-php>namespace App\Request;

use Spiral\Filters\Filter;

class LoginRequest extends Filter
{
    public const SCHEMA = [
        // 绑定参数与请求的关系
        'username' =&gt; 'query:username',
        'password' =&gt; 'query:password'
    ];

    public const VALIDATES = [
        // 定义验证规则
        'username' =&gt; ['notEmpty'],
        'password' =&gt; ['notEmpty']
    ];
}
</code></pre>
<p>在控制器方法中，依赖注入上面定义的 <code>LoginRequest</code>, 系统会自动验证请求并过滤出需要的参数，然后通过 <code>LoginRequest</code> 对象传入：</p>
<pre><code class=language-php>namespace App\Controller;

use App\Request\LoginRequest;

class HomeController
{
    public function index(LoginRequest $request)
    {
        dump($request);
    }
}
</code></pre>
<blockquote>
<p>Use <code>/home/index?username=n&password=p</code> 即可通过上面的验证。</p>
</blockquote>
<p>如果发生参数验证错误，客户端会收到类似下面实例的 <code>application/json</code> 类型的响应结果：</p>
<pre><code class=language-json>{
    &quot;status&quot;: 400,
    &quot;errors&quot;: {
        &quot;username&quot;: &quot;This value is required.&quot;,
        &quot;password&quot;: &quot;This value is required.&quot;
    }
}
</code></pre>
<h3 id=访问权限拦截器>访问权限拦截器</h3>
<p>使用 <code>Spiral\Domain\GuardInterceptor</code> 可以实现 RBAC（Role-Based Access Control: 基于角色的访问控制）预鉴权逻辑（需要安装和激活 <code>spiral/security</code> 组件）。</p>
<pre><code class=language-php>namespace App\Bootloader;

use Spiral\Bootloader\DomainBootloader;
use Spiral\Core\CoreInterface;
use Spiral\Domain\GuardInterceptor;
use Spiral\Security\Actor\Guest;
use Spiral\Security\PermissionsInterface;
use Spiral\Security\Rule;

class AppBootloader extends DomainBootloader
{
    protected const SINGLETONS = [
        CoreInterface::class =&gt; [self::class, 'domainCore']
    ];

    protected const INTERCEPTORS = [
        GuardInterceptor::class
    ];

    public function boot(PermissionsInterface $rbac)
    {
        $rbac-&gt;addRole(Guest::ROLE);
        $rbac-&gt;associate(Guest::ROLE, 'home.*', Rule\AllowRule::class);
        $rbac-&gt;associate(Guest::ROLE, 'home.other', Rule\ForbidRule::class);
    }
}
</code></pre>
<p>可以在控制器方法上用注释（annotation）声明访问权限：</p>
<pre><code class=language-php>namespace App\Controller;

use Spiral\Domain\Annotation\Guarded;

class HomeController
{
    /**
     * @Guarded(&quot;home.index&quot;)
     */
    public function index()
    {
        return 'OK';
    }

    /**
     * @Guarded(&quot;home.other&quot;)
     */
    public function other()
    {
        return 'OK';
    }
}
</code></pre>
<p>用注释声明访问权限时，也可以用 <code>Guarded</code> 注释的 <code>else</code> 属性来指定权限验证失败时应该调用的控制器方法：</p>
<pre><code class=language-php>/**
 * @Guarded(&quot;home.other&quot;, else=&quot;notFound&quot;)
 */
public function other()
{
    return 'OK';
}
</code></pre>
<blockquote>
<p>注意：<code>else</code> 属性可以使用的值包括：<code>notFound</code> 返回 404, <code>forbidden</code> 返回 401, <code>error</code> 返回 500, <code>badAction</code> 返回 400.</p>
</blockquote>
<p>另外，使用 <code>Spiral\Domain\Annotation\GuardNamespace</code> 注释还可以给控制器指定 RBAC 命名空间，这样在各个方法的注释中就可以省略掉控制器前缀。在控制器指定了 RBAC 命名空间的情况下，方法上的 <code>Guarded</code> 注释可以省略参数，安全组件会自动使用 <code>namespace.methodName</code> 作为权限名称。</p>
<pre><code class=language-php>namespace App\Controller;

use Spiral\Domain\Annotation\Guarded;
use Spiral\Domain\Annotation\GuardNamespace;

/**
 * @GuardNamespace(&quot;home&quot;)
 */
class HomeController
{
    /**
     * @Guarded()
     */
    public function index()
    {
        return 'OK';
    }

    /**
     * @Guarded(else=&quot;notFound&quot;)
     */
    public function other()
    {
        return 'OK';
    }
}
</code></pre>
<h4 id=规则上下文>规则上下文</h4>
<p>所有的路由参数（控制器方法参数）可以可以作为规则上下文调用。比如，创建一个规则类：</p>
<pre><code class=language-php>namespace App\Security;

use Spiral\Security\ActorInterface;
use Spiral\Security\RuleInterface;

class SampleRule implements RuleInterface
{
    public function allows(ActorInterface $actor, string $permission, array $context): bool
    {
        return $context['user']-&gt;getID() !== 1;
    }
}
</code></pre>
<p>在引导程序中激活这个规则类：</p>
<pre><code class=language-php>namespace App\Bootloader;

use App\Security\SampleRule;
use Spiral\Bootloader\DomainBootloader;
use Spiral\Core\CoreInterface;
use Spiral\Domain\CycleInterceptor;
use Spiral\Domain\GuardInterceptor;
use Spiral\Security\Actor\Guest;
use Spiral\Security\PermissionsInterface;
use Spiral\Security\Rule;

class AppBootloader extends DomainBootloader
{
    protected const SINGLETONS = [
        CoreInterface::class =&gt; [self::class, 'domainCore']
    ];

    protected const INTERCEPTORS = [
        CycleInterceptor::class,
        GuardInterceptor::class
    ];

    public function boot(PermissionsInterface $rbac)
    {
        $rbac-&gt;addRole(Guest::ROLE);
        $rbac-&gt;associate(Guest::ROLE, 'home.*', SampleRule::class);
        $rbac-&gt;associate(Guest::ROLE, 'home.other', Rule\ForbidRule::class);
    }
}

</code></pre>
<blockquote>
<p>注意：路由器中需要包括被调用的 <code>&lt;id></code> 或者 <code>&lt;user></code> 参数。</p>
</blockquote>
<p>然后控制器方法可以改为：</p>
<pre><code class=language-php>/**
 * @Guarded()
 */
public function index(User $user)
{
    return 'OK';
}
</code></pre>
<p>上述示例中，如果访问该方法（路由）的用户 id 不等于 <code>1</code>，就会鉴权失败，被阻止访问。</p>
<blockquote>
<p>由于这里的 <code>User</code> 依赖 <code>CycleInterceptor</code> 进行解析，所以在领域核心中一定要在 <code>GuardInterceptor</code> 之前启用 <code>CycleInterceptor</code>。</p>
</blockquote>
<h2 id=综合应用>综合应用</h2>
<p>把上面介绍到的所有拦截器综合到一起，就能实现丰富的领域逻辑和安全的控制器方法：</p>
<pre><code class=language-php>use Spiral\Bootloader\DomainBootloader;
use Spiral\Core\CoreInterface;
use Spiral\Domain;

class AppBootloader extends DomainBootloader
{
    protected const SINGLETONS = [
        CoreInterface::class =&gt; [self::class, 'domainCore']
    ];

    protected const INTERCEPTORS = [
        Domain\CycleInterceptor::class,
        Domain\GuardInterceptor::class,
        Domain\FilterInterceptor::class
    ];
}
</code></pre>
</div>
<div class=article-widget>
<div class=post-nav>
<div class=post-nav-item>
<div class=meta-nav>上一页</div>
<a href=/docs/queue/scraper/ rel=next>网站爬虫</a>
</div>
<div class=post-nav-item>
<div class=meta-nav>下一页</div>
<a href=/docs/basics/prototype/ rel=prev>原型开发辅助</a>
</div>
</div>
</div>
</div>
<div class=body-footer>
<p>最近更新于 2020-03-25</p>
<p class=edit-page>
<a href=https://github.com/krwu/spiraldocs/edit/feat/chinese/zh_CN/cookbook/domain-core.md>
<i class="fas fa-pen pr-2"></i>编辑本页
</a>
</p>
</div>
</article>
<footer class=site-footer>
<p class=powered-by><span class=copyright>© 2019 - 2021 <a href=https://studyspiral.cn/>StudySpiral</a> All rights reserved.</span>
</p>
<p class=powered-by>
<a href=http://www.beian.miit.gov.cn/ target=_blank rel="noopener noreferer">粤ICP备14011364号</a>
<a href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=44030402001866" target=_blank rel="noopener noreferer"><img style=display:inline-block src=/img/gaba.png>粤公网安备 44030402001866号</a>
</p>
<p id=webify class=powered-by style=display:flex><span style="margin:0 .2rem 0 0;display:block;height:20px;line-height:20px">Powered by</span> <a href=https://webify.cloudbase.net/ target=_blank rel=noopener>CloudBase Webify</a></p>
<script async defer>window.addEventListener('DOMContentLoaded',()=>{const{host:a}=window.location;(a.toLocaleLowerCase()==='docs.studyspiral.cn'||a.indexOf(".app.tcloudbase.com"))&&(document.getElementById('webify').style.display='flex')})</script>
</footer>
</main>
</div>
</div>
<script src=https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/jquery.imagesloaded/4.1.4/imagesloaded.pkgd.min.js integrity="sha256-lqvxZrPLtfffUl2G/e7szqSvPBILGbwmsGE1MKlOi0Q=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/jquery.isotope/3.0.6/isotope.pkgd.min.js integrity="sha256-CBrpuqrMhXwcLLUd5tvQ4euBHCdh7wGlDfNz8vbu/iI=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js integrity="sha256-yt2kYMy0w8AbtF89WXb2P1rfjcP/HTHLT7097U8Y5b8=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/highlight.min.js integrity="sha256-eOgo0OtLL4cdq7RdwRUiGKLX9XsIJ7nGhWEKbohmVAQ=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/languages/go.min.js></script>
<script>const code_highlighting=!0</script>
<script>const isSiteThemeDark=!1</script>
<script>const search_config={indexURI:"/index.json",minLength:1,threshold:.3},i18n={no_results:"没有找到结果",placeholder:"搜索...",results:"搜索结果"},content_type={post:"文章",project:"项目",publication:"出版物",talk:"演讲"}</script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.1.1/anchor.min.js integrity="sha256-pB/deHc9CGfFpJRjC43imB29Rse8tak+5eXqntO94ck=" crossorigin=anonymous></script>
<script>anchors.add()</script>
<script id=search-hit-fuse-template type=text/x-template>
      <div class="search-hit" id="summary-{{key}}">
      <div class="search-hit-content">
        <div class="search-hit-name">
          <a href="{{relpermalink}}">{{title}}</a>
          <div class="article-metadata search-hit-type">{{type}}</div>
          <p class="search-hit-description">{{snippet}}</p>
        </div>
      </div>
      </div>
    </script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/fuse.js/3.2.1/fuse.min.js integrity="sha256-VzgmKYmhsGNNN4Ph1kMW+BjoYJM2jV5i4IlFoeZA9XI=" crossorigin=anonymous></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/jquery.mark.min.js integrity="sha256-4HLtjeVgH0eIB3aZ9mLYF6E8oU5chNdjU6p6rrXpl9U=" crossorigin=anonymous></script>
<script src=/js/academic.min.6f1005d1a84220e2f466ff3d8e712f31.js></script>
<div id=modal class="modal fade" role=dialog>
<div class=modal-dialog>
<div class=modal-content>
<div class=modal-header>
<h5 class=modal-title>引用</h5>
<button type=button class=close data-dismiss=modal aria-label=Close>
<span aria-hidden=true>&#215;</span>
</button>
</div>
<div class=modal-body>
<pre><code class="tex hljs"></code></pre>
</div>
<div class=modal-footer>
<a class="btn btn-outline-primary my-1 js-copy-cite" href=# target=_blank>
<i class="fas fa-copy"></i> 复制
</a>
<a class="btn btn-outline-primary my-1 js-download-cite" href=# target=_blank>
<i class="fas fa-download"></i> 下载
</a>
<div id=modal-error></div>
</div>
</div>
</div>
</div>
</body>
</html>